package cfca.paperless.client.servlet._2_extend._2_03_SignAutoPdfHash;

import java.io.File;
import java.io.FileInputStream;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;

import cfca.com.itextpdf.text.pdf.PdfArray;
import cfca.com.itextpdf.text.pdf.PdfReader;
import cfca.com.itextpdf.text.pdf.security.PdfPKCS7;
import cfca.paperless.client.bean.SealUserInfo;
import cfca.paperless.client.bean.SignLocation;
import cfca.paperless.client.util.Base64;
import cfca.paperless.client.util.IoUtil;
import cfca.paperless.client.util.ReservedPdfPKCS7Util;
import cfca.sadk.algorithm.common.Mechanism;
import cfca.sadk.lib.crypto.JCrypto;
import cfca.sadk.util.HashUtil;
import cfca.sadk.util.KeyUtil;
import cfca.sadk.util.Signature;
import cfca.sadk.x509.certificate.X509Cert;
import cfca.seal.sadk.DonePdfSeal;
import cfca.seal.sadk.PdfSealVerifier;
import cfca.seal.sadk.PrePdfSealExtra;
import cfca.seal.sadk.security.deferred.ReservedPdfPKCS7;

public class SignAutoPdfHashLocalTest01 {

    /**
     * 本地计算Hash
     * 本地Hash签名
     * 本地合成签名,不使用无纸化服务器或者云端
     * @param args
     * @throws IOException
     * @throws
     */
    public static void main(String[] args) throws Exception {
        try {
            //////////////////////////1、第一次交互,计算hash/////////////////////////
            //待签章pdf
            String filePath = "./TestData/autoCalculatePdfHash.pdf";
            byte[] pdf = FileUtils.readFileToByteArray(new File(filePath));
            //印模图片
            byte[] handwritingImage = null;//FileUtils.readFileToByteArray(new File("./TestData/11111111.png"));
            //获取印章公钥证书
            byte[] x509Cert = FileUtils.readFileToByteArray(new File("./TestData/cert/tl1024.cer"));//从前台传过来的公钥证书
            
            SignLocation signLocation = new SignLocation("代理人签名", "R", 20, -5);//签名位置
            ReservedPdfPKCS7 reservedPdfPKCS7 = new ReservedPdfPKCS7();//创建临时数据保存对象
            //时间戳配置
            String timestampServer[] = new String[]{"http://210.74.41.195/timestamp","",""};
//            PrePdfSealExtra prePdfSealExtra = new PrePdfSealExtra("签合同", "北京");
            SealUserInfo sealUserInfo = new SealUserInfo("zs", "北京", "签合同");//签章人、 地点、 理由
            PrePdfSealExtra prePdfSealExtra = ReservedPdfPKCS7Util.getPrePdfSealExtra(sealUserInfo, "", "");
            String sealLayer2Text = "苏才礼";
            String sealLayer2TextStyle = "font-size:12;font-color:0000FF";//字体大小 字体颜色
            ReservedPdfPKCS7Util.setSealLayer2Text(sealLayer2Text, sealLayer2TextStyle, prePdfSealExtra);
            //计算保留值
            String hashAlogrithmArg = "SHA256";
            byte[] reservedPdf = ReservedPdfPKCS7Util.createReservedPdf(pdf, handwritingImage,0.25f, new X509Cert(x509Cert), hashAlogrithmArg, signLocation, reservedPdfPKCS7, timestampServer, prePdfSealExtra);
            //如下数据需要保存到数据库
            String reservedData = ReservedPdfPKCS7Util.createReservedVar(reservedPdfPKCS7);
            //计算hash
            byte[] pdfSealHash = HashUtil.RSAHashMessageByBC(reservedPdfPKCS7.signatureAttr, new Mechanism(Mechanism.SHA256), false);
            //////////////////////////第一次交互结束/////////////////////////
            
            ///////////////////////2、以下是模拟前台APP的签名/////////////////////////////////////
            PrivateKey privateKey = KeyUtil.getPrivateKeyFromPFX(new FileInputStream("./TestData/cert/tl1024.pfx"), "11111111");
            JCrypto.getInstance().initialize(JCrypto.JSOFT_LIB, null);
            cfca.sadk.lib.crypto.Session mySession = JCrypto.getInstance().openSession(JCrypto.JSOFT_LIB);
            Signature sigUtil = new Signature();
            byte[] signature = null;
            System.out.println("pdfSealHash:" + Base64.toBase64String(pdfSealHash));
            //对hash进行签名
            signature = sigUtil.p1SignByHash(Mechanism.SHA256_RSA, pdfSealHash, privateKey, mySession);
            signature = sigUtil.p7SignByHash(Mechanism.SHA256_RSA, (pdfSealHash), privateKey, new X509Cert(x509Cert), mySession);
            System.out.println("signature:" + new String(signature));
            ///////////////////////模拟前台APP的签名 结束/////////////////////////////////////
            
            ///////////////////////3、以下是合成APP传过来的签名值/////////////////////////////////////
            //恢复reservedPdfPKCS7对象
            reservedPdfPKCS7 = ReservedPdfPKCS7Util.initReservedVar(reservedData);
            reservedPdfPKCS7.signaturePKCS1 = ReservedPdfPKCS7Util.getPKCS1(Base64.decode(signature));
            //合成外部签名
            DonePdfSeal donePdfSeal = new DonePdfSeal();
            byte[] sealedPdfData = donePdfSeal.mergeReservedPdfSeal(new PdfReader(reservedPdf), reservedPdfPKCS7);
            //验证 pdf签章
            PdfSealVerifier pdfSealVerifier = new PdfSealVerifier(sealedPdfData);
            if (!pdfSealVerifier.isPKCS7Valid()) {
                System.out.println("pdfSealVerifier error");
            } else {
                PdfArray uncoverdRange = pdfSealVerifier.getUncoveredByteRange();
                long uncoverdRangeLen = uncoverdRange.asLongArray()[1];
                if (uncoverdRangeLen > 0) {
                    System.out.println(" 单据在盖章之后被修改 ");
                }
                //验证后获取签章时间和证书主题
                Map<String, X509Cert> sigNameCertMap = pdfSealVerifier.getSigNameCertMap();
                Map<String, PdfPKCS7> sigNamePKCS7Map = pdfSealVerifier.getSigNamePKCS7Map();
                List<X509Cert> x509CertList = new ArrayList<X509Cert>(sigNameCertMap.values());
                List<PdfPKCS7> pdfPKCS7List = new ArrayList<PdfPKCS7>(sigNamePKCS7Map.values());
                Calendar calendar = pdfPKCS7List.get(0).getSignDate();
                Date date = calendar.getTime();
                System.out.println("签章时间：" + date.toLocaleString());
                System.out.println("签章证书主题:" + x509CertList.get(0).getSubject());
                System.out.println("签章证书序列号:" + x509CertList.get(0).getStringSerialNumber());
                String outputFilePath = "./TestData/output/signAutoPdfProofHash2-" + new File(filePath).getName() + ".pdf";
                System.out.println("pdfSealVerifier ok " + outputFilePath);
                IoUtil.write(outputFilePath, sealedPdfData);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    
}
